/*----------------------------------------------------------------------------
 *      RL-ARM - SPI
 *----------------------------------------------------------------------------
 *      Name:    RTX_SPI.c
 *      Purpose: SPI Generic Layer Driver
 *      Rev.:    V4.70
 *----------------------------------------------------------------------------
 *      This code is part of the RealView Run-Time Library.
 *      Copyright (c) 2004-2013 KEIL - An ARM Company. All rights reserved.
 *---------------------------------------------------------------------------*/

#include "RTL.h"     /* RTX kernel functions & defines      */
#include "RTX_SPI.h" /* SPI Generic functions & defines     */
#include "stm32f10x.h"

#pragma diag_suppress 550

/* Declare memory pool for SPI messages, both transmit and receive           */
U32 SPI_msg_pool[((sizeof(SPI_msg_t) + 3) / 4) * (SPI_TX_BUF_MAX + SPI_RX_BUF_MAX) + 3];

/* Declare mailbox, for SPI transmit messages                                */
U32 SPI_MBX_tx_ctrl[4 + SPI_TX_BUF_MAX];

/* Declare mailbox, for SPI receive messages                                 */
U32 SPI_MBX_rx_ctrl[4 + SPI_RX_BUF_MAX];

/* Semaphores used for protecting writing to SPI hardware                    */
OS_SEM SPI_wr_sem;

/*----------------------------------------------------------------------------
 *      SPI RTX Generic Driver Functions
 *----------------------------------------------------------------------------
 *  Functions implemented in this module:
 *           SPI_ERROR SPI_mem_init  (void);
 *           SPI_ERROR SPI_setup     (void)
 *           SPI_ERROR SPI_init      (U32 ctrl, U32 baudrate)
 *           SPI_ERROR SPI_start     (U32 ctrl)
 *    static SPI_ERROR SPI_push      (U32 ctrl, SPI_msg *msg, U16 timeout)
 *           SPI_ERROR SPI_send      (U32 ctrl, SPI_msg *msg, U16 timeout)
 *           SPI_ERROR SPI_request   (U32 ctrl, SPI_msg *msg, U16 timeout)
 *           SPI_ERROR SPI_set       (U32 ctrl, SPI_msg *msg, U16 timeout)
 *    static SPI_ERROR SPI_pull      (U32 ctrl, SPI_msg *msg, U16 timeout)
 *           SPI_ERROR SPI_receive   (U32 ctrl, SPI_msg *msg, U16 timeout)
 *           SPI_ERROR SPI_rx_object (U32 ctrl, U32 ch, U32 id, U32 object_para)
 *           SPI_ERROR SPI_tx_object (U32 ctrl, U32 ch,         U32 object_para)
 *---------------------------------------------------------------------------*/

/*--------------------------- SPI_init --------------------------------------
 *
 *  The first time this function is called initialize the memory pool for
 *  SPI messages and setup SPI controllers hardware
 *
 *  Initialize mailboxes for SPI messages and initialize SPI controller
 *
 *  Parameter:  ctrl:       Index of the hardware SPI controller (1 .. x)
 *              baudrate:   Baudrate
 *
 *  Return:     SPI_ERROR:  Error code
 *---------------------------------------------------------------------------*/

SPI_ERROR SPI_init()
{
    SPI_ERROR error_code;
    /* Initialize the Semaphore before the first use */
    os_sem_init(SPI_wr_sem, 1);
    _init_box(SPI_msg_pool, sizeof(SPI_msg_pool), sizeof(SPI_msg_t));
    os_mbx_init(SPI_MBX_tx_ctrl, sizeof(SPI_MBX_tx_ctrl));
    os_mbx_init(SPI_MBX_rx_ctrl, sizeof(SPI_MBX_rx_ctrl));

    error_code = SPI_hw_setup();
    if (error_code != SPI_OK)
        return error_code;

    error_code = SPI_hw_init();
    if (error_code != SPI_OK)
        return error_code;

    SPI_Cmd(SPI3, ENABLE);
    return SPI_OK;
}

/*--------------------------- SPI_push --------------------------------------
 *
 *  Send SPI_msg if hardware is free for sending, otherwise push message to
 *  message queue to be sent when hardware becomes free
 *
 *  Parameter:  ctrl:       Index of the hardware SPI controller (1 .. x)
 *              msg:        Pointer to SPI message to be sent
 *              timeout:    Timeout value for message sending
 *
 *  Return:     SPI_ERROR:  Error code
 *---------------------------------------------------------------------------*/

static SPI_ERROR SPI_push(SPI_msg_t* msg, U16 timeout)
{
    SPI_msg_t* ptrmsg;

    /* Transmit hardware free for send */
    if (SPI_hw_tx_empty() == SPI_OK)
    {
        SPI_hw_wr(msg); /* Send message */
    }
    else
    {
        /* If hardware for sending is busy temporary
         save the message to send mailbox if there is room for it */
        ptrmsg = _alloc_box(SPI_msg_pool);

        if (ptrmsg == NULL)
            return SPI_ALLOC_MEM_ERROR;

        *ptrmsg = *msg;

        /* If message hasn't been sent but timeout expired, deallocate memory  */
        if (os_mbx_send(SPI_MBX_tx_ctrl, ptrmsg, timeout) == OS_R_TMO)
        {
            if (_free_box(SPI_msg_pool, ptrmsg) == 1)
                return SPI_DEALLOC_MEM_ERROR;

            return SPI_TIMEOUT_ERROR;
        }
        else
        {
            /* Check once again if transmit hardware is ready for transmission   */
            if (SPI_hw_tx_empty() == SPI_OK)
            {
                /* Transmit hw free for send */
                if (os_mbx_wait(SPI_MBX_tx_ctrl, (void**)&ptrmsg, 0) == OS_R_TMO)
                {
                    os_sem_send(SPI_wr_sem); /* Return a token back to semaphore  */
                    return SPI_OK;           /* Message was sent from IRQ already */
                }

                if (_free_box(SPI_msg_pool, ptrmsg) == 1)
                {
                    os_sem_send(SPI_wr_sem); /* Return a token back to semaphore  */
                    return SPI_DEALLOC_MEM_ERROR;
                }

                /* Send message */
                SPI_hw_wr(msg);
            }
        }
    }

    return SPI_OK;
}

/*--------------------------- SPI_send --------------------------------------
 *
 *  Send DATA FRAME message, see SPI_push function comment
 *
 *  Parameter:  ctrl:       Index of the hardware SPI controller (1 .. x)
 *              msg:        Pointer to SPI message to be sent
 *              timeout:    Timeout value for message sending
 *
 *  Return:     SPI_ERROR:  Error code
 *---------------------------------------------------------------------------*/

SPI_ERROR SPI_send(SPI_msg_t* msg, U16 timeout)
{
    return (SPI_push(msg, timeout));
}

/*--------------------------- SPI_pull --------------------------------------
 *
 *  Pull first received and unread SPI_msg from receiving message queue
 *
 *  Parameter:  ctrl:       Index of the hardware SPI controller (1 .. x)
 *              msg:        Pointer where SPI message will be read
 *              timeout:    Timeout value for message receiving
 *
 *  Return:     SPI_ERROR:  Error code
 *---------------------------------------------------------------------------*/

static SPI_ERROR SPI_pull(SPI_msg_t* msg, U16 timeout)
{
    SPI_msg_t* ptrmsg;

    /* Wait for received message in mailbox */
    if (os_mbx_wait(SPI_MBX_rx_ctrl, (void**)&ptrmsg, timeout) == OS_R_TMO)
        return SPI_TIMEOUT_ERROR;

    /* Copy received message from mailbox to address given in function parameter msg */
    *msg = *ptrmsg;

    /* Free box where message was kept */
    if (_free_box(SPI_msg_pool, ptrmsg) == 1)
        return SPI_DEALLOC_MEM_ERROR;

    return SPI_OK;
}

/*--------------------------- SPI_receive -----------------------------------
 *
 *  Read received message, see SPI_pull function comment
 *
 *  Parameter:  ctrl:       Index of the hardware SPI controller (1 .. x)
 *              msg:        Pointer where SPI message will be read
 *              timeout:    Timeout value for message receiving
 *
 *  Return:     SPI_ERROR:  Error code
 *---------------------------------------------------------------------------*/

SPI_ERROR SPI_receive(SPI_msg_t* msg, U16 timeout)
{
    return (SPI_pull(msg, timeout));
}

/*----------------------------------------------------------------------------
 * end of file
 *---------------------------------------------------------------------------*/
